package com.cta.dao.impl;

import java.math.BigInteger;
import java.util.ArrayList;
import java.util.List;

import javax.persistence.EntityManager;
import javax.persistence.EntityManagerFactory;
import javax.persistence.PersistenceException;
import javax.persistence.Query;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.orm.jpa.JpaCallback;
import org.springframework.orm.jpa.support.JpaDaoSupport;

import com.cta.dao.BasePersistence;
import com.cta.entity.BaseEntity;
import com.cta.utils.SupportLoaderDelegate;

@SuppressWarnings("unchecked")
public class BasePersistenceImpl extends JpaDaoSupport implements BasePersistence {

	@Autowired
	public void setFactory(EntityManagerFactory entityManagerFactory) {
		super.setEntityManagerFactory(entityManagerFactory);
	}

	public <T extends BaseEntity> List<T> getList(String sql, Object... params) {
		try {
			return getJpaTemplate().find(sql, params);
		} catch (Exception e) {
			e.printStackTrace();
			return new ArrayList<T>();
		}
	}

	public <T extends BaseEntity> List<T> getList(String sql, SupportLoaderDelegate<T> loader, Object... params) {
		List<T> result = getList(sql, params);
		loader.load(result);
		return result;
	}

	public <T extends BaseEntity> List<T> getList(final String queryString, final int firstResult, final int maxResult,
			final Object... params) {
		JpaCallback<List<T>> callback = new JpaCallback<List<T>>() {
			public List<T> doInJpa(EntityManager em) throws PersistenceException {
				Query queryObject = em.createQuery(queryString);
				if (params != null) {
					for (int i = 0; i < params.length; i++) {
						queryObject.setParameter(i + 1, params[i]);
					}
				}
				queryObject.setFirstResult(firstResult);
				queryObject.setMaxResults(maxResult);
				return queryObject.getResultList();
			}
		};
		try {
			return (List<T>) getJpaTemplate().execute(callback);
		} catch (Exception e) {
			e.printStackTrace();
			return new ArrayList<T>(0);
		}
	}

	public <T extends BaseEntity> List<T> getList(final String queryString, SupportLoaderDelegate<T> loader,
			final int firstResult, final int maxResult, final Object... params) {
		JpaCallback<List<T>> callback = new JpaCallback<List<T>>() {
			public List<T> doInJpa(EntityManager em) throws PersistenceException {
				Query queryObject = em.createQuery(queryString);
				if (params != null) {
					for (int i = 0; i < params.length; i++) {
						queryObject.setParameter(i + 1, params[i]);
					}
				}
				queryObject.setFirstResult(firstResult);
				queryObject.setMaxResults(maxResult);
				return queryObject.getResultList();
			}
		};
		try {
			List<T> result = (List<T>) getJpaTemplate().execute(callback);
			loader.load(result);
			return result;
		} catch (Exception e) {
			e.printStackTrace();
			return new ArrayList<T>(0);
		}
	}

	public <T extends BaseEntity> List<T> getListWithNativeSql(final Class<T> clazz, final String queryString,
			SupportLoaderDelegate<T> loader, final int firstResult, final int maxResult, final Object... params) {
		JpaCallback<List<T>> callback = new JpaCallback<List<T>>() {
			public List<T> doInJpa(EntityManager em) throws PersistenceException {
				Query queryObject = em.createNativeQuery(queryString, clazz);
				if (params != null) {
					for (int i = 0; i < params.length; i++) {
						queryObject.setParameter(i + 1, params[i]);
					}
				}
				queryObject.setFirstResult(firstResult);
				queryObject.setMaxResults(maxResult);
				return queryObject.getResultList();
			}
		};
		try {
			List<T> result = (List<T>) getJpaTemplate().execute(callback);
			if (loader != null) {
				loader.load(result);
			}
			return result;
		} catch (Exception e) {
			e.printStackTrace();
			return new ArrayList<T>(0);
		}
	}
	
	public <T> List<T> getListWithNativeSql(final String queryString,
			SupportLoaderDelegate<T> loader, final int firstResult, final int maxResult, final Object... params) {
		JpaCallback<List<T>> callback = new JpaCallback<List<T>>() {
			public List<T> doInJpa(EntityManager em) throws PersistenceException {
				Query queryObject = em.createNativeQuery(queryString);
				if (params != null) {
					for (int i = 0; i < params.length; i++) {
						queryObject.setParameter(i + 1, params[i]);
					}
				}
				queryObject.setFirstResult(firstResult);
				queryObject.setMaxResults(maxResult);
				return queryObject.getResultList();
			}
		};
		try {
			List<T> result = (List<T>) getJpaTemplate().execute(callback);
			if (loader != null) {
				loader.load(result);
			}
			return result;
		} catch (Exception e) {
			e.printStackTrace();
			return new ArrayList<T>(0);
		}
	}

	public <T extends BaseEntity> T findById(Class<T> clazz, Object id) {
		return getJpaTemplate().find(clazz, id);
	}

	public <T extends BaseEntity> T findById(Class<T> clazz, Object id, SupportLoaderDelegate<T> loader) {
		T result = getJpaTemplate().find(clazz, id);
		if (result != null && loader != null) {
			loader.load(result);
		}
		return result;
	}

	public <T extends BaseEntity> T save(T entity) {
		try {
			if (entity.getId() != null && entity.getId() <= 0) {
				entity.setId(null);
			}
			if (entity.getId() == null) {
				getJpaTemplate().persist(entity);
			} else {
				getJpaTemplate().merge(entity);
			}
			return entity;
		} catch (Exception e) {
			e.printStackTrace();
			return null;
		}
	}

	public <T extends BaseEntity> void insert(T entity) {
		getJpaTemplate().persist(entity);
	}

	public <T extends BaseEntity> void update(T entity) {
		getJpaTemplate().merge(entity);
	}

	public <T extends BaseEntity> void delete(T entity) {
		BaseEntity e = getJpaTemplate().getReference(entity.getClass(), entity.getId());
		if (e != null) {
			getJpaTemplate().remove(e);
		}
	}

	public int execute(final String queryString, final Object... values) {
		JpaCallback<Integer> callback = new JpaCallback<Integer>() {
			public Integer doInJpa(EntityManager em) throws PersistenceException {
				Query queryObject = em.createQuery(queryString);
				if (values != null) {
					for (int i = 0; i < values.length; i++) {
						queryObject.setParameter(i + 1, values[i]);
					}
				}
				return queryObject.executeUpdate();
			}
		};
		try {
			return (Integer) getJpaTemplate().execute(callback);
		} catch (Exception e) {
			e.printStackTrace();
			return -1;
		}
	}

	public <T extends BaseEntity> List<T> findByProperty(Class<T> entityClass, SupportLoaderDelegate<T> loader,
			String propertyName, final Object value) {
		try {
			final String queryString = String.format("select model from %s model where model.%s= ?1",
					entityClass.getSimpleName(), propertyName);
			List<T> result = getJpaTemplate().find(queryString, value);
			if (result != null && loader != null) {
				loader.load(result);
			}
			return result;
		} catch (RuntimeException re) {
			throw re;
		}
	}

	public <T extends BaseEntity> List<T> findByProperty(Class<T> entityClass, String propertyName, final Object value) {
		return findByProperty(entityClass, null, propertyName, value);
	}

	public <T extends BaseEntity> T findSingleObject(Class<T> entityClass, String propertyName, final Object value) {
		List<T> list = findByProperty(entityClass, propertyName, value);
		if (list.size() > 0)
			return list.get(0);
		return null;
	}

	public <T extends BaseEntity> T findSingleObject(String queryString, Object... params) {
		List<T> list = getList(queryString, 0, 1, params);
		if (list.size() > 0)
			return list.get(0);
		return null;
	}

	public long countObjects(String countQuery, Object... params) {
		List<Long> list = getJpaTemplate().find(countQuery, params);
		if (list.size() > 0)
			return list.get(0);
		return 0;
	}
	
	public long sumObjects(String countQuery, Object... params) {
		List<Long> list = getJpaTemplate().find(countQuery, params);
		if (list.size() > 0) {
			if(list.get(0) != null) {
				return list.get(0);
			}
		}
		return 0;
	}

	public long countObjectsWithNativeSql(final String queryString, final Object... params) {
		JpaCallback<Long> callback = new JpaCallback<Long>() {
			public Long doInJpa(EntityManager em) throws PersistenceException {
				Query queryObject = em.createNativeQuery(queryString);
				if (params != null) {
					for (int i = 0; i < params.length; i++) {
						queryObject.setParameter(i + 1, params[i]);
					}
				}
				Long value = ((BigInteger) queryObject.getSingleResult()).longValue();
				return value;
			}
		};
		try {
			return getJpaTemplate().execute(callback);
		} catch (Exception e) {
			e.printStackTrace();
			return 0;
		}
	}

	public int getLoginAccountId() {
		return 1;
	}
}
